"""Transport abstraction for VANET simulation."""
import json
import queue
import socket
import threading
import time
from typing import Callable, Dict, Any, Optional
from .. import vanet_sim

class InProcessTransport:
    def __init__(self, topology_provider=None):
        self.handlers: Dict[str, Callable[[str, dict], None]] = {}
        self.topology_provider = topology_provider  # function returning adjacency {id: [neighbors]}
        self.node_positions = {}

    def register_node(self, node_id: str, handler: Callable[[str, dict], None]):
        self.handlers[node_id] = handler

    def send(self, from_id: str, to_id: str, message: dict):
        if to_id in self.handlers:
            self.handlers[to_id](from_id, message)

    def broadcast(self, from_id: str, message: dict, radius: Optional[float] = None):
        if self.topology_provider is None:
            for nid, handler in self.handlers.items():
                if nid != from_id:
                    handler(from_id, message)
            return
        topo = self.topology_provider()
        for neighbor in topo.get(from_id, []):
            if neighbor in self.handlers:
                self.handlers[neighbor](from_id, message)

class SocketTransport:
    def __init__(self, host='127.0.0.1', base_port=50000):
        self.host = host
        self.base_port = base_port
        self.handlers: Dict[str, Callable[[str, dict], None]] = {}
        self.threads = {}
        self.sockets = {}

    def register_node(self, node_id: str, handler: Callable[[str, dict], None]):
        port = self.base_port + len(self.handlers)
        srv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        srv.bind((self.host, port))
        srv.listen(5)
        self.handlers[node_id] = handler
        self.sockets[node_id] = (srv, port)

        def accept_loop():
            while True:
                conn, addr = srv.accept()
                data = conn.recv(65536)
                try:
                    payload = json.loads(data.decode('utf-8'))
                except Exception:
                    payload = {}
                src = payload.get('_from')
                msg = payload.get('message', {})
                handler(src, msg)
                conn.close()

        t = threading.Thread(target=accept_loop, daemon=True)
        t.start()
        self.threads[node_id] = t

    def send(self, from_id: str, to_id: str, message: dict):
        if to_id not in self.sockets:
            return
        _, port = self.sockets[to_id]
        payload = json.dumps({'_from': from_id, 'message': message}).encode('utf-8')
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        try:
            s.connect((self.host, port))
            s.sendall(payload)
        finally:
            s.close()

    def broadcast(self, from_id: str, message: dict, radius: Optional[float] = None):
        for nid in self.sockets:
            if nid != from_id:
                self.send(from_id, nid, message)
